package com.tambapps.p2p.fandem

import com.tambapps.p2p.fandem.exception.CorruptedFileException
import com.tambapps.p2p.fandem.exception.SharingException
import com.tambapps.p2p.fandem.util.FileUtils
import com.tambapps.p2p.fandem.util.TransferListener
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream

abstract class FileSharer( @JvmField
                           protected val listener: TransferListener? = null) {
    @Throws(IOException::class)
    protected fun share(
        inputStream: InputStream,
        outputStream: OutputStream,
        bufferSize: Int,  // nullable expectedChecksum
        fileName: String,
        totalBytes: Long,
        expectedChecksum: String?
    ) {
        val buffer = ByteArray(bufferSize)
        var lastProgress = 0
        var bytesProcessed: Long = 0
        var progress: Int
        var count: Int
        val digest = FileUtils.sha256MessageDigest
        while (inputStream.read(
                buffer, 0,
                if (totalBytes - bytesProcessed > buffer.size) buffer.size else (totalBytes - bytesProcessed).toInt()
            ).also { count = it } > 0
        ) {
            bytesProcessed += count.toLong()
            outputStream.write(buffer, 0, count)
            digest.update(buffer, 0, count)
            progress =
                Math.min((MAX_PROGRESS - 1).toLong(), MAX_PROGRESS * bytesProcessed / totalBytes)
                    .toInt()
            if (progress != lastProgress && listener != null) {
                lastProgress = progress
                listener.onProgressUpdate(fileName, progress, bytesProcessed, totalBytes)
            }
        }
        if (bytesProcessed != totalBytes) {
            throw SharingException("Transfer was not properly finished")
        } else listener?.onProgressUpdate(fileName, MAX_PROGRESS, totalBytes, totalBytes)
        if (expectedChecksum != null && FileUtils.bytesToHex(digest.digest()) != expectedChecksum) {
            throw CorruptedFileException()
        }
    }

    companion object {
        const val DEFAULT_BUFFER_SIZE = 8192
        private const val MAX_PROGRESS = 100
    }
}
